home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
assemblr
/
tools
/
trace
/
trace.asm
< prev
next >
Wrap
Assembly Source File
|
1986-04-08
|
70KB
|
2,932 lines
page 60,132
.lfcond
title "TRACE - Interrupt Tracer"
subttl Introduction
page
comment \
TRACE is an INT tracer. It traps INT's, traces the registers at the entry
to the INT, executes the INT itself, and then traces the regs at exit from
the INT. It also allows the user at the keyboard to see all of this traced
information, either at the screen or on the printer.
The INT's that are to be traced are specified in the source code (see config
section below). Once defined, they may be enabled or disabled from the
keyboard.
<<< REBOOT AS SOON AS POSSIBLE AFTER INVOKING TRACE!!! >>>
This is not foolproof or bugfree. We use it 'cause it's a terrific tool
for deprotecting copy-protected software. You use it at your own risk.
When you've gotten a printout of the trace activity that interests you,
reboot your system. Don't say that we didn't warn you.
Enjoy.
\
page
code segment para 'code'
assume cs:code,ds:code
org 0100h
start: jmp init
;********************************************************
;
; Definition of one interrupt control table
;
;********************************************************
ICT struc
ICT_flags db ? ;See below
ICT_intnum db ? ;interrupt # this table belongs to
ICT_AH_lo db ? ;lower AH limit to trace
ICT_AH_hi db ? ;upper AH limit to trace
ICT_orig_hndlr dd ? ;cs:ip of original handler
ICT_hits dw ? ;# traces made for this ICT
ICT_num db ? ;ICT number (0-7)
ICT ends
;
; Equates for flags byte of ICT
;
F_ACTIVE equ 10000000b ;Bit 7 = this ICT is active
F_RET equ 01000000b ;Bit 6 = This INT exits via RET
F_RET2 equ 00100000b ;Bit 5 = This INT exits via RET2
F_IRET equ 00010000b ;Bit 4 = This INT exits via IRET
F_ENABLE equ 00001000b ;Bit 3 = Tracing enabled for this ICT
F_FCB equ 00000100b ;Bit 2 = enable FCB/ASCII traces for INT 21h
F_ROM equ 00000010b ;Bit 1 = exclude ROM invocations of this INT
F_BELOW equ 00000001b ;Bit 0 = exclude invokers below us (DOS etc)
page
;*******************************************************
; B E G I N C O N F I G U R A T I O N
;*******************************************************
;
; Set trace_size to number of bytes to set aside for trace table.
;
trace_size equ 30000
;
; Set peri_int to the interrupt number (usually 060H) to be used
; by Periscope to invoke our reporting routines.
;
peri_int equ 060h
;
; Set prt_scr non-zero to assemble code that allows control via SHIFT-PrtSc.
;
prt_scr equ 1
;
; Set use_prt non-zero to start up using the printer instead of the screen
;
use_prt equ 0
;
; Set num_feeds to number of extra Linefeeds to send to printer to jack
; up the paper enough to let you read it, after display of menus etc.
;
num_feeds equ 10
;
; Define interrupts to be traced by filling in the following ICT's.
; Note that there are only 8. That's the max that can be traced.
; Note that the F_ACTIVE flag must be set in used ICT's, and off in
; those that aren't defining something that you want traced.
;
; Don't mess with the last 3 fields in the ICT!!! Leave 'em as-is.
;
; It is EXTREMELY important that you specify how the interrupt exits.
; Set exactly one of the following flag bits:
;
; F_RET Interrupt exits via simple far RET, leaving original interrupts
; on the stack for the caller to pop. INT's 025H and 026H are prime
; examples.
;
; F_RET2 Interrupt exits via RET 2, dropping original flags and returning
; flags as set by interrupt handler. INT 021H (and anybody else
; that uses flags like CARRY or ZERO to reflect results) does this.
;
; F_IRET Interrupt exits via IRET, restoring original flags from stack.
; Hardware interrupt handlers do this, and many ROM BIOS routines.
;
; Note that some interrupts may exit differently, depending on the specific
; function requested. You may want to define several ICT's for a given
; interrupt, each handling a different AH range.
;
;
ict0 ICT <F_ACTIVE+F_RET2+F_ENABLE+F_FCB,021H,0dh,0ffh,0,0,0> ;DOS funcs 0Dh thru FFh
ict1 ICT <F_ACTIVE+F_RET2+F_ENABLE,013H,0,0ffh,0,0,1> ;ROM BIOS INT 13h (all)
ict2 ICT <F_ACTIVE+F_RET2,021H,0,0ch,0,0,2> ;DOS funcs 00h thru 0Ch
ict3 ICT <F_ACTIVE+F_RET+F_ENABLE,025h,0,0ffh,0,0,3> ;RAW disk I/O
ict4 ICT <F_ACTIVE+F_RET+F_ENABLE,026h,0,0ffh,0,0,4> ;RAW disk I/O
ict5 ICT <0,0,0,0,0,0,5> ;unused
ict6 ICT <0,0,0,0,0,0,6> ;unused
ict7 ICT <0,0,0,0,0,0,7> ;unused
;*******************************************************
; E N D C O N F I G U R A T I O N
;*******************************************************
page
;
; Trace entry byte zero (the type byte) identifies entry as follows:
;
; Bits 7-4 = trace type
;
; 0000 = BEFORE int was executed
; 0001 = AFTER int was executed
; 0010 = FCB referenced by an INT 21h
; 0011 = ASCIIZ referenced by an INT 21h
;
; Bit 3 = RESERVED
;
; Bits 2-0 = ICT # (0-7) which made this trace record
;
; Record formats are as follows:
;
BEFORE struc ;BEFORE trace entry
B_type db ? ;see above
B_int db ? ;INT # that was trapped
B_ax dw ? ;Regs BEFORE executing INT
B_bx dw ?
B_cx dw ?
B_dx dw ?
B_es dw ?
B_ds dw ?
B_ss dw ?
B_sp dw ?
B_si dw ?
B_di dw ?
B_bp dw ?
B_cs dw ? ;CS of invoker of INT
B_ip dw ? ;IP of invoker
BEFORE ends
AFTER struc ;AFTER record type
A_type db ? ;see above
A_int db ? ;INT # that was executed
A_ax dw ? ;Regs AFTER executing INT
A_bx dw ?
A_cx dw ?
A_dx dw ?
A_es dw ?
A_ds dw ?
A_si dw ?
A_di dw ?
A_bp dw ?
A_flags dw ? ;Flags AFTER doing INT
AFTER ends
FCB struc ;INT 21h FCB record
FCB_type db ? ;see above
FCB_int db ? ;INT # that was executed (21h)
FCB_drive db ? ;drive field of FCB
FCB_name db "????????" ;filename field of FCB
FCB_ext db "???" ;extension field of FCB
FCB ends
ASCIIZ struc ;INT 21h ASCIIZ record
AZ_type db ? ;see above
AZ_int db ? ;INT # that was executed (21h)
AZ_str db "?????????????????????????????????????????????????????????????????"
ASCIIZ ends
subttl Resident Storage
page
;
; Things defined here are present even after we become resident and
; exit to DOS.
;
;
; Definitions used by print_edit()
;
Edit_End equ 0f0h ;End of input line
Edit_Byte equ 0f1h ;Next byte is 8-bit value to be printed in hex
Edit_Word equ 0f2h ;Next 2 bytes are 16-bit value to be printed in hex
Edit_Line equ 0f3h ;Next 2 bytes are address of string for print_line()
Edit_Call equ 0f4h ;Next byte is AH arg, next 2 are DX arg,
;and next 2 are subroutine address to call
Edit_Dec8 equ 0f5h ;Next byte is 8-bit value to be printed as decimal
Edit_Dec16 equ 0f6h ;Next 2 bytes are 16-bit value to be printed as decimal
Edit_Skip equ 0f7h ;Ignore this byte (don't display it)
;********************************************************
;
; Index table pointing to all ICT's
;
;********************************************************
ict_index label word
dw offset ict0
dw offset ict1
dw offset ict2
dw offset ict3
dw offset ict4
dw offset ict5
dw offset ict6
dw offset ict7
;********************************************************
;
; Index table pointing to all handlers
;
;********************************************************
hndlr_index label word
dw offset handler0
dw offset handler1
dw offset handler2
dw offset handler3
dw offset handler4
dw offset handler5
dw offset handler6
dw offset handler7
;********************************************************
;
; Trace table itself, with plenty of room
;
;********************************************************
next_byte dw offset trace_table ;address of next entry
trace_table db trace_size dup (0)
last_byte db 80 dup (0) ;sloppy way of handling overflow
;********************************************************
;
; Table of sizes for each trace record type. Must be in same
; order as record types themselves.
;
;********************************************************
rec_sizes label word
dw size BEFORE
dw size AFTER
dw size FCB
dw size ASCIIZ
;********************************************************
;
; Table of INT 21h functions that include an FCB pointer in DS:DX
;
;********************************************************
FCB_table label byte
db 0fh,10h,11h,12h,13h,14h,15h,16h,17h,21h,22h,23h,24h,27h,28h
FCB_end label byte
;********************************************************
;
; Table of INT 21h functions that include an ASCIIZ pointer in DS:DX
;
;********************************************************
ASCIIZ_table label byte
db 4bh,3ch,3dh,41h,43h,4eh,56h,5ah,5bh,39h,3ah,3bh
ASCIIZ_end label byte
;********************************************************
;
; Misc storage
;
;********************************************************
our_cs dw 0 ;our CS (not for segment checking)
test_cs dw 0 ;our normalized CS (for segment checking)
long_addr dd 0 ;for long JMP's and CALLs
our_ICT dw 0 ;for quick save of our ICT pointer
our_flags db 0 ;for quick save of our ICT flags
prt_base dw 0 ;base I/O address of printer
prt_flag db 0 ;non-zero to send output to printer
db 255 dup (0) ;stack for Periscope Int handler
our_tos dw 0 ;top of that stack
stack_inuse db 0 ;non-zero when above stack is in use
save_ss dw 0 ;for stack-swapping
save_sp dw 0
subttl Interrupt Trappers and Tracing
page
;********************************************************
;
; Interrupt handler entry points for each ICT
;
;********************************************************
handler macro ictloc
cli ;*** NO INTERRUPTS!!! ***
push bp ;save stack pointer, so that...
mov bp,sp ;...we can ref things via BP
push bx ;set BX to point to ICT
mov bx,offset cs:ictloc
jmp short int_common ;goto common code
endm
interrupt proc far
handler0:
handler ict0
handler1:
handler ict1
handler2:
handler ict2
handler3:
handler ict3
handler4:
handler ict4
handler5:
handler ict5
handler6:
handler ict6
handler7:
handler ict7
int_common:
push ax
;********************************************************
;
; Common code for all trapped INT's.
;
; At this point:
;
; BX holds ICT address.
; BP points to stack as follows:
;
; AX
; BX
; (BP) ----> BP
; +2 IP of caller
; +4 CS of caller
; +6 FLAGS of caller
;
;********************************************************
test cs:[bx].ICT_flags,F_ENABLE ;tracing enabled for this ICT?
jz no_trace ;no, don't trace it
cmp ah,cs:[bx].ICT_AH_lo ;is AH within bounds?
jb no_trace ;no, don't trace it
cmp ah,cs:[bx].ICT_AH_hi
ja no_trace ;no, don't trace it
;
; See if we should check caller's CS:IP
;
test cs:[bx].ICT_flags,F_BELOW+F_ROM
jz int_common3 ;no segment checks to be made
mov ax,2[bp] ;get caller's IP
shr ax,1 ;prepare to normalize segment #
shr ax,1
shr ax,1
shr ax,1
add ax,4[bp] ;add in segment
;
; ------ AX now equals normalized segment #
;
test cs:[bx].ICT_flags,F_BELOW
jz int_common2 ;don't check for invoker below us
cmp ax,cs:test_cs ;is caller below us?
jb no_trace ;yes, don't trace
int_common2:
test cs:[bx].ICT_flags,F_ROM
jz int_common3 ;don't check for invoker in ROM
cmp ax,0c000h ;is caller in ROM?
jae no_trace ;yes, don't trace
int_common3:
;
; See if we have room for this trace
;
mov ax,cs:next_byte ;get address of next entry
push ax
add ax,size BEFORE ;add size of this record
cmp ax,offset cs:last_byte ;would record fit?
pop ax
jb yes_trace ;yes, there's room
no_trace:
;
; We are not to trace this INT, for whatever reason.
; Just go to original handler, and return to caller (not to us).
;
mov ax,word ptr cs:[bx].ICT_orig_hndlr
mov word ptr cs:long_addr,ax
mov ax,word ptr cs:[bx].ICT_orig_hndlr+2
mov word ptr cs:long_addr+2,ax
pop ax
pop bx
pop bp
jmp cs:long_addr ;let original handler return to caller
yes_trace:
;
; We are to proceed with trace of this INT. Make trace entry.
;
push es
push di
;
; At this point:
;
; AX holds offset to next trace entry.
; BX holds ICT address.
; BP points to stack as follows:
;
; DI
; ES
; AX
; BX
; (BP) ----> BP
; +2 IP of caller
; +4 CS of caller
; +6 FLAGS of caller
;
mov di,ax ;set ES:DI to next trace entry
mov ax,cs
mov es,ax
cld ;forward!!!
mov ah,cs:[bx].ICT_intnum ;get interrupt # being traced
mov al,cs:[bx].ICT_num ;get ICT #, make BEFORE record type
stosw
mov ax,-4[bp] ;original AX
stosw
mov ax,-2[bp] ;original BX
stosw
mov ax,cx
stosw
mov ax,dx
stosw
mov ax,-6[bp] ;original ES
stosw
mov ax,ds
stosw
mov ax,ss
stosw
mov ax,bp ;original SP
add ax,2
stosw
mov ax,si
stosw
mov ax,-8[bp] ;original DI
stosw
mov ax,[bp] ;original BP
stosw
mov ax,4[bp] ;caller's CS
stosw
mov ax,2[bp] ;caller's IP
stosw
mov cs:next_byte,di ;save spot for next trace entry
inc cs:[bx].ICT_hits ;bump number of traces made for this ICT
;
; We're done with the BEFORE trace. See if we are to do an FCB or ASCIIZ
; trace record.
;
test cs:[bx].ICT_flags,F_FCB
jz no_FCB ;no, we are definitely not supposed to
cmp cs:[BX].ICT_intnum,021h ;is this an INT 21h?
jnz no_FCB ;no, can't trace it then
;
; Search through ASCIIZ function table, to see if function that was called
; is one that contains an ASCIIZ pointer in DS:DX
;
mov ax,-4[bp] ;get AX at time of call
push cx
mov di,offset cs:ASCIIZ_table
mov cx,offset cs:ASCIIZ_end
sub cx,di ;CX now has size of table
mov al,ah ;get function to AL
repnz scasb ;see if it's in table
pop cx
jz trace_ASCIIZ ;it's there, so do ASCIIZ trace
;
; Search through FCB function table, to see if function that was called
; is one that contains an FCB pointer in DS:DX
;
push cx
mov di,offset cs:FCB_table
mov cx,offset cs:FCB_end
sub cx,di ;CX now has size of table
repnz scasb ;see if it's in table
pop cx
jnz no_FCB ;no FCB or ASCIIZ trace called for
;
; We are to do trace of FCB pointed to by DS:DX
;
mov al,00100000b ;trace record number for FCB trace
mov ah,size FCB ;size of record
jmp short trace_common ;rest is common code
trace_ASCIIZ:
;
; We are to do trace of ASCIIZ string pointed to by DS:DX
;
mov al,00110000b ;trace record number for ASCIIZ trace
mov ah,size ASCIIZ ;size of record
trace_common:
;
; Copy bytes from DS:DX to new ASCIIZ or FCB trace record.
;
; At this point:
;
; AL holds trace record type, properly positioned in bits 7-4
; AH holds size of record that we're doing (the full record)
;
sub ah,2 ;minus two bytes for record header
mov di,cs:next_byte ;see if there's room...
push di
push ax
mov al,ah ;get record size to AX
xor ah,ah
add di,ax
cmp di,offset cs:last_byte
pop ax
pop di
jae no_FCB ;no room
push ax ;save AX over this
mov ah,cs:[bx].ICT_intnum ;start header with interrupt #
or al,cs:[bx].ICT_num ;add ICT number to trace type
stosw ;start new record with it
pop ax ;restore AX
push cx
push si
mov si,dx ;access DS:DX as DS:SI
mov cl,ah ;record size to CX
xor ch,ch
rep movsb ;that's how many to copy
pop si
pop cx
mov cs:next_byte,di ;save offset to next record
no_FCB: ;end of FCB/ASCIIZ tracing
;
; Having traced all of those, now invoke original interrupt handler. Have
; it return to us, not the original caller of the interrupt.
;
mov cs:our_ICT,bx ;save ICT pointer for a nanosecond
mov al,cs:[bx].ICT_flags ;save copy of flags that we can get to
mov cs:our_flags,al
mov ax,word ptr cs:[bx].ICT_orig_hndlr
mov word ptr cs:long_addr,ax
mov ax,word ptr cs:[bx].ICT_orig_hndlr+2
mov word ptr cs:long_addr+2,ax
pop di
pop es
pop ax
pop bx
pop bp
push cs:our_ICT ;save ICT pointer on stack
test cs:our_flags,F_RET ;should we push flags?
jnz no_flags ;no, cause they'd be left on stack
pushf ;yes, give handler some flags to drop
no_flags:
call cs:long_addr ;invoke original handler
;
; We're back from the real interrupt handler, and can make the "after" trace.
; Our ICT address is on stack.
;
sti ;give world a crack at interrupts
nop
nop
cli ;*** NO INTERRUPTS!!! ***
push bp ;establish stack reference
mov bp,sp
pushf ;save resultant flags
push es
push di
push ax
push bx
;
; At this point:
;
; BX
; AX
; DI
; ES
; FLAGS (as returned by real interrupt)
; (BP) --------> BP
; ICT address
; IP of caller
; CS of caller
; FLAGS of original caller
;
mov bx,2[bp] ;recover ICT address
mov di,cs:next_byte ;room for "after" trace entry?
push di
add di,size AFTER
cmp di,offset cs:last_byte
pop di
jae no_after ;no, skip it
mov ax,cs ;make ES:DI point to next entry
mov es,ax
cld ;forward!!!
mov ah,cs:[bx].ICT_intnum ;get interrupt #
mov al,cs:[bx].ICT_num ;get ICT #, make AFTER record type
or al,00010000b
stosw
mov ax,-8[bp] ;AX at int's return
stosw
mov ax,-10[bp] ;BX at int's return
stosw
mov ax,cx
stosw
mov ax,dx
stosw
mov ax,-4[bp] ;ES at int's return
stosw
mov ax,ds
stosw
mov ax,si
stosw
mov ax,-6[bp] ;DI at int's return
stosw
mov ax,[bp] ;BP at int's return
stosw
mov ax,-2[bp] ;FLAGS at int's return
stosw
mov cs:next_byte,di ;save offset to next entry
no_after:
;
; All done making "after" trace, or we've skipped it cause there was
; no room for it.
;
; Now just exit back to the original caller.
;
mov al,cs:[bx].ICT_flags ;save flags where we can get to them
mov cs:our_flags,al
pop bx
pop ax
pop di
pop es
popf
pop bp
pop cs:our_ICT ;drop ICT address without affecting flags
;
; HOW we exit is extremely important. We must exit the same way that the
; real interrupt does.
;
pushf ;save current flags in case we return them
test cs:our_flags,F_RET
jnz exit_ret
test cs:our_flags,F_RET2
jnz exit_ret2
;
; Assume IRET.
;
exit_iret:
popf ;exit via IRET, reloading original flags
STI ;Allow interrupts now
iret
exit_ret2:
popf ;exit via RET 2, discarding original flags
STI ;Allow interrupts now
ret 2
exit_ret:
popf ;exit via far RET, leaving original flags
STI ;Allow interrupts now
ret
interrupt endp
subttl Support routines - Printer & Screen I/O
page
;*****************************************
;
; Select video for subsequent output
;
;*****************************************
selvideo proc near
mov prt_flag,0
ret
selvideo endp
;*****************************************
;
; Select printer for subsequent output
;
;*****************************************
selprint proc near
push ax
mov ax,prt_base ;do we have a printer?
or al,ah
mov prt_flag,al ;al is non-zero if we do
pop ax
ret
selprint endp
;********************************************************
;
; Output AL to printer or screen, depending on prt_flag.
;
;********************************************************
print proc near
push dx
push cx
push bx
push ax
; ----- See if it should go to printer
test prt_flag,0ffh ;send it to printer?
jnz print1 ;yes
print0:
;
; Send char to video via INT 010H
;
mov bl,1
mov ah,14 ;"Write TTY" func
int 010h
clc ;show no I/O error
jmp short print9
print1:
;----- Send it to printer
mov dx,prt_base ;get printer base I/O address
inc dx ;up to status port
mov ah,al ;save char in ah
xor cx,cx ;init timeout ticker
print2:
in al,dx ;get status
test al,080h ;is printer busy?
jnz print5 ;no, proceed to send char
;
; We're not immediately ready. Some printers require more of a wait than
; the simple 64K loop found in CX. So here's a time waster that you may
; want to tailor to your printer.
;
mov al,8 ;greatly extend timeout value
print3:
dec al
jnz print3
loop print2 ;wait for whole timeout
stc ;set carry for timeout
jmp short print9 ;and exit
print5:
dec dx ;down to data reg
mov al,ah ;recover char to be sent
out dx,al ;put it on data lines
inc dx ;up to control port
inc dx
mov al,0dh ;set strobe low
out dx,al
mov al,0ch ;set strobe high again
out dx,al
clc ;show no error
print9:
;
; At this point, CARRY is SET if we were going to the printer and had an
; I/O error.
;
jnc print10 ;no error
call selvideo ;error, so switch to video
pop ax ;recover AL
push ax
jmp print0 ;go send it to video
print10:
pop ax
pop bx
pop cx
pop dx
ret
print endp
;********************************************************
;
; Output binary AL as 2 hex digits
;
;********************************************************
print_hex proc near
push bx
push ax
mov bl,al ;isolate HO nibble
shr bl,1
shr bl,1
shr bl,1
shr bl,1
and bx,0fh
mov al,hextab[bx] ;xlit to hex char
call print ;print 1st char
pop ax
push ax
mov bl,al ;isolate LO nibble
and bx,0fh
mov al,hextab[bx] ;xlit to hex char
call print ;print 2nd char
pop ax
pop bx
ret
print_hex endp
hextab db '0123456789ABCDEF'
;********************************************************
;
; Output binary word AX as 4 hex digits
;
;********************************************************
print_word proc near
xchg ah,al ;get HO half to AL
call print_hex ;print 1st 2 chars
xchg ah,al ;get LO half back to AL
call print_hex ;print 2nd 2 chars
ret
print_word endp
;********************************************************
;
; Output binary word AX as 4 hex digits, plus a blank
;
;********************************************************
print_wordb proc near
push ax
call print_word
mov al,' '
call print
pop ax
ret
print_wordb endp
;*****************************************
;
; Print CRLF.
;
;*****************************************
crlf proc near
push ax
mov al,0dh
call print
mov al,0ah
call print
pop ax
ret
crlf endp
;*****************************************
;
; Print string at DS:DX, up to "$" character.
;
;*****************************************
print_line proc near
push si
push ax
cld ;forward!
mov si,dx ;DS:SI = string
print_line2:
lodsb ;get next byte to print
cmp al,'$' ;terminating char?
jz print_line9 ;yes, exit
call print ;print this char
jmp print_line2 ;continue till "$"
print_line9:
pop ax
pop si
ret
print_line endp
;*****************************************
;
; Print DX (HO), AX (LO) as xxxx:xxxx.
;
;*****************************************
print_seg proc near
push ax
mov ax,dx ;get HO word first
call print_word
mov al,':' ;show seperator too
call print
pop ax ;recover LO word
call print_word
ret
print_seg endp
;********************************************************************
;
; Print a line at [DX], edited.
;
; Line may contain Edit_xxxx escape characters, as defined above.
;
;********************************************************************
print_edit proc near
push si
push dx
push cx
push bx
push ax
mov si,dx ;use DS:SI to read line
cld ;forward!!!
print_edit2:
lodsb ;get next byte of line
cmp al,Edit_Byte ;binary byte to expand?
jnz print_edit3 ;no
lodsb ;yes, get 8-bit value
call print_hex ;print it as hex
print_edit2b:
mov al,'H' ;tack "H" for HEX after it
print_edit2c:
call print
jmp print_edit2 ;go get next char
print_edit3:
cmp al,Edit_Word ;16-bit binary to expand?
jnz print_edit4 ;no
lodsw ;yes, get 16-bit word
call print_word ;display as hex
jmp print_edit2b ;follow with 'H' and continue
print_edit4:
cmp al,Edit_Call ;call another routine?
jnz print_edit5 ;no
lodsb ;yes, get AH argument
mov bh,al ;save for a nano...
lodsw ;get DX argument
mov dx,ax
lodsw ;get address to call
mov cx,ax
mov ah,bh ;recover AH argument to use
push si ;save our precious SI
call cx ;call the routine
pop si
jmp print_edit2 ;go get next char
print_edit5:
cmp al,Edit_Dec8 ;8-bit decimal value?
jnz print_edit6 ;no
lodsb ;yes, get 8-bit byte
xor ah,ah ;clear HO byte
print_edit5b:
call print_dec ;print AX as decimal
jmp print_edit2 ;go get next input char
print_edit6:
cmp al,Edit_Dec16 ;16-bit decimal value?
jnz print_edit7 ;no
lodsw ;yes, get 16-bit byte
jmp print_edit5b ;print it and go get next char
print_edit7:
cmp al,Edit_End ;end of input string?
jnz print_edit2c ;no, assume ASCII char and print it
pop ax
pop bx
pop cx
pop dx
pop si
ret
print_edit endp
;**************************************************
;
; Print AX in decimal, suppressing leading zeroes
;
;**************************************************
print_dec proc near
push dx
push cx
push bx
push ax
mov cx,10 ;divisor
xor dx,dx
div cx ;DL=units, AX = answer
mov bh,dl ;save units
xor dx,dx
div cx ;DL=tens, AX = answer
mov bl,dl ;get tens
or bx,03030h ;make into 2 ASCII digits
mov word ptr dec_buf+3,bx
div cl ;AH=hunds, AL = answer
mov bh,ah ;save hundreds
xor ah,ah
div cl ;AH=thous, AL = ten_thousands
mov bl,ah ;get thous
or bx,03030h ;make into 2 ASCII digits
mov word ptr dec_buf+1,bx
or al,030h ;make ten-thousands into ASCII digit
mov byte ptr dec_buf,al
;
; Now edit out leading zeroes by advancing BX to 1st non-zero
;
mov bx,offset dec_buf
mov cx,4 ;max # to suppress
print_dec2:
cmp byte ptr [bx],'0'
jnz print_dec5 ;found non-zero, so exit
inc bx ;up to next digit
loop print_dec2
print_dec5:
;
; All set. Print from [BX] on...
;
mov dx,bx
call print_line
pop ax
pop bx
pop cx
pop dx
ret
print_dec endp
dec_buf db "99999$"
;********************************************************************
;
; Print one string from a table of possible strings.
;
; On entry: AH holds selector
; DX holds table address
;
; Each table entry is as follows:
;
; db <selector>,"string",<term>
;
; where:
; <selector> is 8-bit byte that is compared with AH. If it
; matches, then this string is printed.
;
; "string" is the string to be printed
;
; <term> is the terminating character, as follows:
;
; 00H : end of this string
; 80H : end of this string, and end of table too
;
; If no <selector> matches AH, then "????" is printed.
;
;********************************************************************
table_print proc near
push si
push dx
push cx
push bx
push ax
mov si,dx ;use DS:SI to read table
cld ;forward!!!
table_print2:
lodsb ;get next selector
cmp al,ah ;does it match AH?
jnz table_print5 ;no, skip to next one
table_print3:
;
; We have found string to print. Output it until a terminator is found.
;
lodsb ;get byte of string
test al,07fh ;terminator?
jz table_print9 ;yes, exit
call print ;no, print this char
jmp table_print3
table_print5:
;
; Not this selector. Skip over string till terminator, then go peek
; at next selector.
;
lodsb ;get byte of string
test al,07fh ;terminator?
jnz table_print5 ;no, keep skipping
;
; We have terminator at end of skipped string. It may be end of whole table...
;
cmp al,080h ;end of table?
jnz table_print2 ;no, go check next selector
mov dx,offset huh ;yes, print "????" message cause match not found
call print_line
table_print9:
pop ax
pop bx
pop cx
pop dx
pop si
ret
table_print endp
huh db "????$"
;*********************************************
;
; Issue extra linefeeds if we're going to the printer. This
; moves the paper up enough to be read.
;
; This should be called before any input, and whenever output is
; generally finished.
;
;*********************************************
feed proc near
push ax
push cx
test prt_flag,0ffh ;are we going to the printer?
jz feed9 ;no, just exit
mov cx,num_feeds ;# linefeeds to do
jcxz feed9 ;none, so exit
feed2:
call crlf
loop feed2
feed9:
pop cx
pop ax
ret
feed endp
subttl Menu Handling
page
;*********************************************
;
; Get uppercase keyboard char to AL. AH is clobbered.
;
;*********************************************
key proc near
mov ah,0 ;use ROM BIOS to read keyboard
int 016h
cmp al,'a' ;lowercase char?
jb key9 ;no
cmp al,'z'
ja key9 ;likewise no
and al,0dfh ;yes, convert to uppercase
key9:
ret
key endp
;*********************************************
;
; Reset all ICT hits to zero, and restart trace buffer
;
;*********************************************
zap_hits proc near
push si
push ax
push bx
push cx
mov cx,8 ;Number of ICT's
xor si,si ;start with # 0
cli ;no interrupts!
zap_hits2:
mov bx,ict_index[si] ;[BX] --> ICT
mov [bx].ICT_hits,0
add si,2 ;up to next ICT
loop zap_hits2 ;till we've done all of them
mov next_byte,offset trace_table
sti ;interrupts OK now
pop cx
pop bx
pop ax
pop si
ret
zap_hits endp
page
;*********************************************
;
; Handle main menu selection whose ASCII keypress is in AL.
;
; Returns: CARRY SET if we should loop back to main menu.
; CARRY CLEAR to exit.
;
;*********************************************
do_main proc near
push ax
cmp al,'P' ;select printer?
jnz do_main1 ;no
call selprint ;yes, do it
stc ;"Loop back to main menu" flag
jmp short do_main9
do_main1:
cmp al,'S' ;select screen?
jnz do_main2 ;no
call selvideo ;yes, do it
stc ;"Loop back to main menu" flag
jmp short do_main9
do_main2:
cmp al,'T' ;Dump Traces?
jnz do_main3 ;no
call do_traces ;yes, do it
stc ;"Loop back to main menu" flag
jmp short do_main9
do_main3:
cmp al,'E' ;Enable ICT?
jnz do_main4 ;no
mov al,F_ENABLE ;yes, get bit value to set/clear
do_main3b:
call do_enable ;enable/disable F_ENABLE per AL
stc ;"Loop back to main menu" flag
jmp short do_main9
do_main4:
cmp al,'D' ;Disable ICT?
jnz do_main5 ;no
mov al,0 ;yes, get bit value to set/clear
jmp do_main3b
do_main5:
cmp al,'L' ;List ICT's?
jnz do_main6 ;no
call disp_active ;yes, do it
stc ;"Loop back to main menu" flag
jmp short do_main9
do_main6:
cmp al,'C' ;Clear trace table?
jnz do_main7 ;no
call zap_hits ;yes, do it
stc ;"Loop back to main menu" flag
jmp short do_main9
do_main7:
cmp al,'Q' ;Quit?
jnz do_main8 ;no
clc ;"Exit" flag
jmp short do_main9
do_main8:
cmp al,'F' ;F_FCB toggle?
jnz do_main8B ;no
call do_fcb ;yes, toggle something
stc ;"Loop back to main menu" flag
jmp short do_main9
do_main8b:
; ------ Unknown selection
stc ;"Loop back to main menu" flag
do_main9:
pop ax
ret
do_main endp
;**********************************************
;
; Pick ICT's with which to do something.
;
; This is called to select ICT for various operations.
;
; On entry, DX holds address of question (no CRLF's) to be asked.
;
; Returns: CARRY SET if user selected ABORT to cancel the caller's operation
;
; CARRY CLEAR if AL has been set to 8-bit pattern, with each
; bit from 0 to 7 representing an ICT (0-7) that was selected.
;
;**********************************************
pick_ict proc near
push bx
push cx
push dx
mov byte ptr pick_map,0 ;init to nobody selected
pick_ict1:
;
; Put up our selection menu
;
call crlf
pop dx ;display caller's question
push dx
call print_line
mov dx,offset pick_menu ;put up our menu
call print_line
;
; Fill in choices already made, as if he had typed them
;
mov cx,8 ;# ICT's
mov ah,byte ptr pick_map ;AH has bitmap
mov al,'0' ;AL holds ASCII '0' - '7'
pick_ict1b:
test ah,1 ;Is this ICT selected?
jz pick_ict1c ;no
call print ;yes, show corresponding ASCII char
pick_ict1c:
inc al ;Bump ASCII char
shr ah,1 ;get next bit to test
loop pick_ict1b ;till done all 8
call feed ;eject paper on printer
pick_ict2:
;
; Get and handle next keypress
;
call key
cmp al,'0' ;ICT number?
jb pick_ict3 ;no
cmp al,'7'
ja pick_ict3 ;no
call print ;yes, echo it
;
; Convert this ASCII char to bitmap bit, and add to our map
;
call bin_to_bit ;comes back in AL
or byte ptr pick_map,al ;add this new bit into pattern
jmp pick_ict2 ;go get next keypress
pick_ict3:
cmp al,'L' ;List ICT's?
jnz pick_ict4 ;no
call disp_active ;yes, show all active ICT's
jmp pick_ict1 ;give our menu again
pick_ict4:
cmp al,'R' ;Restart?
jnz pick_ict5 ;no
mov byte ptr pick_map,0 ;yes, clear map
jmp pick_ict1 ;give new menu
pick_ict5:
cmp al,'G' ;Go with choices?
jnz pick_ict6 ;no
pick_ict5b:
mov al,byte ptr pick_map ;yes, get choices as bitmap
clc ;tell caller to use it
jmp short pick_ict9 ;exit
pick_ict6:
cmp al,0dh ;Carriage Return?
jz pick_ict5b ;yes, same as "Go"
cmp al,'A' ;Abort operation?
jnz pick_ict7 ;no
pick_ict6b:
stc ;tell caller to abort
jmp short pick_ict9 ;exit
pick_ict7:
cmp al,1bh ;ESCAPE?
jz pick_ict6b ;yes, same as "Abort"
; ------ Unknown choice
jmp pick_ict2 ;go get next keypress
pick_ict9:
pop dx
pop cx
pop bx
ret
pick_ict endp
pick_menu db 0dh,0ah
db "0-7 picks ICT (L)ist ICT's (A)bort (R)estart (G)o with choices"
db 0dh,0ah,":$"
pick_map db 0 ;bitmap of selected ICT's
;*********************************************
;
; Handle "Traces" main menu option
;
;*********************************************
do_traces proc near
push ax
push dx
mov dx,offset trace_menu ;put up our menu
call print_line
call feed ;extra CRLF's for printer
call key ;get his selection
cmp al,'A' ;dump All?
jnz do_traces2 ;no
mov al,0ffh ;yes, get bitmap for all ICT's
jmp short do_traces7 ;dump 'em
do_traces2:
cmp al,'S' ;Selected ICT's?
jnz do_traces9 ;no, so exit
mov dx,offset trace_prompt ;point to question to be used
call pick_ict ;get ICT's as bitmap in AL
jc do_traces9 ;user wants to forget about it
do_traces7:
;
; Do dump, with AL holding bitmap of ICT's that are to be included
;
call dump_buf ;with AL already set
do_traces9:
pop dx
pop ax
ret
do_traces endp
trace_menu db 0dh,0ah
db "Display (A)ll or (S)elected ICTs' traces:$"
trace_prompt db "Pick ICT's whose traces are to be included in dump$"
;*********************************************
;
; Set or Clear F_ENABLE.
;
; On entry, AL holds bit value for F_ENABLE (i.e. - ON or OFF).
;
; This routine asks user for ICT's to be enabled or disabled.
;
;*********************************************
do_enable proc near
push si
push dx
push cx
push bx
push ax ;push him last so we can get to him
mov dx,offset enable_prompt ;Assume "Enable"
test al,F_ENABLE ;are we enabling?
jnz do_enable1 ;yes, we have right message
mov dx,offset disable_prompt ;Use "Disable" message
do_enable1:
call pick_ict ;get ICT's to be affected
jc do_enable9 ;user wants to forget it
mov byte ptr enable_map,al ;save bitmap of ICT's to be done
xor si,si ;start with ICT #0
mov cx,8 ;number of ICT's to look at
do_enable2:
test byte ptr enable_map,1 ;Should this ICT be done?
jz do_enable5 ;no
mov bx,ict_index[si] ;yes, point to ICT
cli ;*** NO INTERRUPTS!!! ***
pop ax ;get F_ENABLE value
push ax
and al,F_ENABLE ;isolate our bit
mov ah,[bx].ICT_flags ;get current flags value
and ah,F_ENABLE XOR 0ffh ;turn off our bit
or ah,al ;set it per caller's desire
mov [bx].ICT_flags,ah ;replace it in ICT
STI ;*** INTERRUPTS OK NOW ***
do_enable5:
add si,2 ;up to next ICT
shr byte ptr enable_map,1 ;get next ICT's bitmap bit to Bit 0
loop do_enable2 ;till we've looked at all ICT's
do_enable9:
pop ax
pop bx
pop cx
pop dx
pop si
ret
do_enable endp
enable_prompt db "Pick ICT's to have tracing ENABLED$"
disable_prompt db "Pick ICT's to have tracing DISABLED$"
enable_map db 0 ;bitmap of ICT's to be altered
;*********************************************
;
; Toggle F_FCB in some ICT's.
;
;*********************************************
do_fcb proc near
push si
push dx
push cx
push bx
push ax ;push him last so we can get to him
mov dx,offset fcb_toggle
call pick_ict ;get ICT's to be affected
jc do_fcb9 ;user wants to forget it
xor si,si ;start with ICT #0
mov cx,8 ;number of ICT's to look at
do_fcb2:
test al,1 ;Should this ICT be done?
jz do_fcb5 ;no
mov bx,ict_index[si] ;yes, point to ICT
xor [bx].ICT_flags,F_FCB ;toggle current setting
do_fcb5:
add si,2 ;up to next ICT
shr al,1 ;get next ICT's bitmap bit to Bit 0
loop do_fcb2 ;till we've looked at all ICT's
do_fcb9:
pop ax
pop bx
pop cx
pop dx
pop si
ret
do_fcb endp
fcb_toggle db "Pick ICT's to have F_FCB toggled$"
subttl Reporting Routines
page
;**************************************************
;
; Dump trace buffer for ICT's represented by bitmap in AL.
;
; If bit n in AL is set, then ICT n's trace records are to be included
; in dump.
;
;**************************************************
dump_buf proc near
push di
push si
push dx
push cx
push bx
push ax ;push bitmap last so that we can get to it
xor di,di ;di is printed line counter
mov si,offset trace_table ;start at front of buf
dump_buf2:
cmp si,next_byte ;done whole buffer?
jae dump_buf9 ;yes, exit
;
; Let a keypress interrupt us
;
mov ah,1 ;ROM BIOS "Check for keypress" func
int 016h ;keypress present?
jnz dump_buf9 ;yes, exit
mov al,[si].B_type ;get ICT #
call bin_to_bit ;convert to bitmap bit
pop bx ;peek at caller's requested bitmap
push bx
and bl,al ;is this ICT included in caller's bitmap?
jz dump_buf5 ;no, skip it
;
; See if it's time for title line
;
test di,07h ;every 8 lines
jnz dump_buf4 ;not time for title line
mov dx,offset dump_title ;print title line
call print_line
dump_buf4:
call dump_rec ;dump this record
inc di ;bump # lines printed
dump_buf5:
;
; Skip over this record, to next one. To do that, we need to know what
; type of record it is, so that we know how big a record
; we have to skip over.
;
mov bl,[si].B_type ;get trace record type
and bx,11110000b ;isolate type itself
shr bx,1 ;develop type times 2
shr bx,1
shr bx,1
add si,rec_sizes[bx] ;add record size to current pointer
jmp dump_buf2 ;continue till buffer exhausted
dump_buf9:
pop ax
pop bx
pop cx
pop dx
pop si
pop di
ret
dump_buf endp
dump_title db 0dh,0ah
db 0dh,0ah
db "INT # AX BX CX DX ES DS SI DI BP SS SP CS:IP"
db 0dh,0ah
db "--- - ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---------$"
;***********************************************
;
; Given binary number (0-7) in AL, return bitmap in AL with corresponding
; bit (Bit 0 thru Bit 7) set.
;
; AH is zeroed.
;
;***********************************************
bin_to_bit proc near
mov ah,al ;AH is counter, from 7 to 0
mov al,1 ;AL is bitmap, Bit 0 to Bit 7
and ah,7 ;constrain input
jz bin_to_bit9 ;binary was 0, so return with Bit 0 set
bin_to_bit3:
add al,al ;shift bitmap left 1 bit
dec ah ;dec count by one
jnz bin_to_bit3
bin_to_bit9:
ret
bin_to_bit endp
;***********************************************
;
; Dump trace record at [SI]. This routine prints the common
; header information, then calls specific routines to expand
; details.
;
;***********************************************
dump_rec proc near
push si
push dx
push cx
push bx
push ax
call crlf
mov al,[si].B_int ;get Interrupt #
call print_hex ;show interrupt #
mov al,'H'
call print
mov al,' '
call print
mov al,[si].B_type ;get ICT #
and al,7
or al,'0' ;make into ASCII digit
call print
mov al,' '
call print
mov bl,[si].B_type ;get trace type
and bx,11110000b ;isolate type of trace
shr bx,1 ;develop type times 2
shr bx,1
shr bx,1
mov bx,word ptr dump_table[bx]
call bx ;call proper specific routine
pop ax
pop bx
pop cx
pop dx
pop si
ret
dump_rec endp
;
; Table of routines to handle various record types
;
dump_table label word
dw offset dump_before ;record type 0 = BEFORE
dw offset dump_after ;record type 1 = AFTER
dw offset dump_fcb ;record type 2 = FCB
dw offset dump_asciiz ;record type 3 = ASCIIZ
;***********************************************
;
; Dump BEFORE record at [SI]
;
;***********************************************
dump_before proc near
push si
push dx
push cx
push bx
push ax
mov al,'B' ;Display "B" for BEFORE
call print
mov al,' ' ;plus blank after
call print
mov ax,[si].B_ax
call print_wordb
mov ax,[si].B_bx
call print_wordb
mov ax,[si].B_cx
call print_wordb
mov ax,[si].B_dx
call print_wordb
mov ax,[si].B_es
call print_wordb
mov ax,[si].B_ds
call print_wordb
mov ax,[si].B_si
call print_wordb
mov ax,[si].B_di
call print_wordb
mov ax,[si].B_bp
call print_wordb
mov ax,[si].B_ss
call print_wordb
mov ax,[si].B_sp
call print_wordb
mov dx,[si].B_cs
mov ax,[si].B_ip
call print_seg
;
; Try to interpret this BEFORE record, to make reading easier
;
call interp
pop ax
pop bx
pop cx
pop dx
pop si
ret
dump_before endp
;***********************************************
;
; Dump AFTER record at [SI]
;
;***********************************************
dump_after proc near
push si
push dx
push cx
push bx
push ax
mov al,'A' ;Display "A" for AFTER
call print
mov al,' ' ;plus blank after
call print
mov ax,[si].A_ax
call print_wordb
mov ax,[si].A_bx
call print_wordb
mov ax,[si].A_cx
call print_wordb
mov ax,[si].A_dx
call print_wordb
mov ax,[si].A_es
call print_wordb
mov ax,[si].A_ds
call print_wordb
mov ax,[si].A_si
call print_wordb
mov ax,[si].A_di
call print_wordb
mov ax,[si].A_bp
call print_wordb
;
; Now expand flags byte for clarity
;
mov dx,[si].A_flags ;hold flags in DX
mov si,offset dump_flags ;SI = next flag's name
mov bx,0fd5h ;mask of valid bits in flags word
mov cx,12 ;# bits to walk through
cld ;forward!!!
dump_after2:
test bx,1 ;is this a valid flag bit?
jz dump_after4 ;no, move to next one
lodsb ;yes, get next name
test dx,1 ;is bit set?
jnz dump_after3 ;yes, use name
mov al,' ' ;no, use blank
dump_after3:
call print ;print flag name or space
dump_after4:
shr dx,1 ;shift flags so next flag is in bit 0
shr bx,1 ;ditto for mask
loop dump_after2 ;till done all 12 bits
pop ax
pop bx
pop cx
pop dx
pop si
ret
dump_after endp
dump_flags db "CPAZSTIDO"
;***********************************************
;
; Dump FCB record at [SI]
;
;***********************************************
dump_fcb proc near
push si
push dx
push cx
push bx
push ax
mov al,[si].FCB_drive ;display drive # as number
mov byte ptr fcb_drv,al
mov dx,offset fcb_line ;and put up rest of header
call print_edit
add si,3 ;skip to filename field
mov cx,8 ;max # chars to display
cld ;forward!!!
dump_fcb2:
lodsb ;get byte of filename
cmp al,020h ;control char or blank?
jbe dump_fcb3b ;yes, we're done with name
call print ;no, display char as-is
loop dump_fcb2 ;till 8 done or early exit
jmp short dump_fcb4
dump_fcb3: ;skip over rest of filename
lodsb
dump_fcb3b:
loop dump_fcb3
dump_fcb4: ;output extension too
mov al,'.' ;seperate it with period
call print
mov cx,3 ;# extension bytes to print
dump_fcb5:
lodsb ;get byte of extension
cmp al,020h ;control char?
jb dump_fcb6 ;yes, skip it
call print ;no, use as-is
dump_fcb6:
loop dump_fcb5
pop ax
pop bx
pop cx
pop dx
pop si
ret
dump_fcb endp
fcb_line label byte
db "FCB Drive:"
db Edit_Dec8
fcb_drv db 0
db " Filename: "
db Edit_End
;***********************************************
;
; Dump ASCIIZ record at [SI]
;
;***********************************************
dump_asciiz proc near
push si
push dx
push cx
push bx
push ax
mov dx,offset asciiz_line ;put up header
call print_line
add si,2 ;skip to start of ASCIIZ text
mov cx,size ASCIIZ ;max # chars to display
sub cx,2 ;(minus 2 for header)
cld ;forward!!!
dump_asciiz5:
lodsb ;get byte of extension
or al,al ;NUL terminator?
jz dump_asciiz9 ;yes, exit
cmp al,020h ;control char?
jb dump_asciiz6 ;yes, skip it
call print ;no, use as-is
dump_asciiz6:
loop dump_asciiz5
dump_asciiz9:
pop ax
pop bx
pop cx
pop dx
pop si
ret
dump_asciiz endp
asciiz_line label byte
db "ASCIIZ: $"
;*****************************************
;
; Display what we know about ICT # AL (0-7).
;
;*****************************************
ict_dump proc near
push dx
push bx
push ax
and ax,7 ;edit ICT #
mov bx,ax ;get ICT #
shl bx,1
mov bx,ict_index[bx] ;[BX] --> ICT itself
or al,030h ;make ASCII digit for message
mov byte ptr ict_msg1a,al ;insert it into message
mov dx,offset ict_msg1 ;"ICT #n at ..."
call print_line
mov dx,ds ;display seg:offset of ICT
mov ax,bx
call print_seg
mov dx,offset ict_ena ;show whether enabled or disabled
test [bx].ICT_flags,F_ENABLE
jnz ict_dump2 ;got right message
mov dx,offset ict_dis ;get other message
ict_dump2:
call print_line ;display "ENABLED" or "DISABLED"
mov dx,offset ict_msg2 ;"INT xxH "
call print_line
mov al,[bx].ICT_intnum ;display interrupt #
call print_hex
mov dx,offset ict_msg3 ;"AH range ll/hh"
call print_line
mov al,[bx].ICT_AH_lo ;display AH range lower limit
call print_hex
mov al,'/' ;add seperator
call print
mov al,[bx].ICT_AH_hi ;display AH range upper limit
call print_hex
mov al,'*' ;display '*' if FCB/ASCIIZ set
test [bx].ICT_flags,F_FCB
jnz ict_dump3 ;it's set
mov al,' ' ;not set, so use blank
ict_dump3:
call print
mov dx,offset ict_msg4 ;"Exit: RET/RET2/IRET"
call print_line
mov al,[bx].ICT_flags ;interpret exit type
mov dx,offset ict_exit ;get to first 6-char message
test al,F_RET
jz ict_dump5 ;not this one
call print_line
ict_dump5:
add dx,6 ;up to next 6-char exit name
test al,F_RET2
jz ict_dump6 ;not this one
call print_line
ict_dump6:
add dx,6 ;up to next 6-char exit name
test al,F_IRET
jz ict_dump7 ;not this one
call print_line
ict_dump7:
mov dx,offset ict_msg4a ;"Hits: "
call print_line
mov ax,[bx].ICT_hits
call print_dec
pop ax
pop bx
pop dx
ret
ict_msg1 db 0dh,0ah,"ICT#"
ict_msg1a db "0 @ $"
ict_msg2 db " INT $"
ict_msg3 db "H AH:$"
ict_msg4 db " Exit:$"
ict_msg4a db "Hits: $"
ict_exit db "RET $" ;6-char exit type names
db "RET2 $"
db "IRET $"
ict_ena db " ENABLED $"
ict_dis db " DISABLED$"
ict_dump endp
;*********************************************
;
; Display all active ICT's
;
;*********************************************
disp_active proc near
push si
push ax
push bx
push cx
mov cx,8 ;Number of ICT's
xor si,si ;start with # 0
disp_active2:
mov bx,ict_index[si] ;[BX] --> ICT
test [bx].ICT_flags,F_ACTIVE ;Is this ICT active?
jz disp_active5 ;no, skip it
mov ax,si ;yes, develop ICT # 0-7
shr ax,1
call ict_dump ;display it
disp_active5:
add si,2 ;up to next ICT
loop disp_active2 ;till we've done all of them
pop cx
pop bx
pop ax
pop si
ret
disp_active endp
subttl Interpretation - Misc Routines
page
;********************************************************************
;* *
;* This file contains the routines that interpret selected *
;* BEFORE trace records and print out sensible summaries of *
;* their meanings. This sure beats having to read a lotta hex *
;* function codes. *
;* *
;* The main routine - interp() - is called just after we've *
;* printed all of the trace record in hex. If this record is *
;* one that we know about, we should now print a one-line *
;* interpretation of the record. This is done via lower-level *
;* routines called by interp() per the INT in the record. *
;* *
;********************************************************************
;********************************************************************
;
; Interpret BEFORE trace record at [SI].
;
;********************************************************************
interp proc near
push bx
push ax
mov ah,[SI].B_int ;get INT type
mov bx,offset interp_tab ;point to table of handlers
interp2:
cmp ah,[bx] ;does this handler go with this INT?
jnz interp5 ;no
mov bx,1[bx] ;yes, get handler's address
call bx ;call that handler
jmp interp9 ;exit
interp5:
add bx,3 ;up to next entry in table
cmp bx,offset interp_end ;searched whole table yet?
jb interp2 ;no, try next one
interp9:
pop ax
pop bx
ret
interp endp
;
; Table of interpreters for various interrupts.
;
; Each entry is as follows:
;
; db <intnum> ;interrupt number
; dw offset <handler> ;address of handler to interpret this int's record
;
;
interp_tab label byte
db 013h ;INT 13h is Diskette I/O
dw offset interp_13 ;handler for INT 13h
db 021h ;INT 21h is DOS Function Handler
dw offset interp_21 ;handler for INT 21h
db 025h ;INT 025H is DOS ABSOLUTE DISK READ
dw offset interp_25
db 026h ;INT 026H is DOS ABSOLUTE DISK WRITE
dw offset interp_25 ;uses same interpreter
interp_end label byte ;end of table
subttl Interpretation - INT 13 (Diskette I/O)
page
;
; Tables used to interpret INT 13h
;
Floppy_or_Hard db 0,"Floppy:",0
db 080h,"Fixed:",080h
Cyl_or_Track db 0,"Track:",0
db 080h,"Cyl:",080h
int13_functab label byte
db 0,"Reset Disk",0
db 1,"Read Status",0
db 2,"Read to ES:BX",0
db 3,"Write from ES:BX",0
db 4,"Verify",0
db 5,"Format Track per ES:BX",0
db 6,"Format & Set Bad Sects",0
db 7,"Format Drive from Track",0
db 8,"Get Drive Params",0
db 9,"Init Drive Params",0
db 10,"Read Long",0
db 11,"Write Long",0
db 12,"Seek",0
db 13,"Alt Disk Reset",0
db 14,"Read Sect Buf",0
db 15,"Write Sect Buf",0
db 16,"Test Drive Ready",0
db 17,"Recalibrate",0
db 18,"Ram Diagnostic",0
db 19,"Drive Diagnostic",0
db 20,"Internal Diagnostic",0
db 21,"Get Disk Type",0
db 22,"Change Status",0
db 23,"Set Disk Type",080h
int13_line label byte
db 0dh,0ah," "
db Edit_Call
int13_F_or_H db 0
dw offset Floppy_or_Hard
dw offset table_print
db Edit_Dec8
int13_drive db 0
db " Head:"
db Edit_Dec8
int13_head db 0
db " "
db Edit_Call
int13_C_or_T db 0
dw offset Cyl_or_Track
dw offset table_print
db Edit_Dec16
int13_cyl dw 0
db " Sect:"
db Edit_Dec8
int13_sect db 0
db " #Sects:"
db Edit_Dec8
int13_numsects db 0
db " "
db Edit_Call
int13_func db 0
dw offset int13_functab
dw offset table_print
db Edit_End
;**************************************************
;
; Interpret INT 13h BEFORE trace record at [SI]
;
;**************************************************
interp_13 proc near
push dx
push cx
push ax
mov dx,[SI].B_dx ;get DX at time of INT
mov al,dl ;Get drive #
and al,080h ;isolate floppy/hard bit
mov int13_F_or_H,al ;use it to select device name...
mov int13_C_or_T,al ;...as well as cylinders/tracks
and dl,07fh ;isolate drive #
mov int13_drive,dl
mov int13_head,dh ;store head #
mov cx,[SI].B_cx ;get CX at time of INT
xor ax,ax ;calc 10-bit cylinder #
mov al,cl
shl ax,1
shl ax,1
mov al,ch
mov int13_cyl,ax ;save as word
and cl,00111111b ;isolate sector #
mov int13_sect,cl
mov ax,[SI].B_ax ;get AX at time of INT
mov int13_numsects,al
mov int13_func,ah
mov dx,offset int13_line ;now print edited line
call print_edit
pop ax
pop cx
pop dx
ret
interp_13 endp
subttl Interpretation - INT 21h (DOS)
page
;
; Tables used in interpreting INT 21h
;
int21_functab label byte
db 0,"Terminate program",0
db 1,"Console input w/echo to AL",0
db 2,"Display Output of DL",0
db 3,"AUX input to AL",0
db 4,"AUX output from DL",0
db 5,"Printer output from DL",0
db 6,"Input to AL (DL=0FFh) or Display DL",0
db 7,"Raw keyboard input w/o echo to AL",0
db 8,"Console input w/o echo to AL",0
db 9,"Print string at DS:DX till '$'",0
db 0ah,"Buffered console input to DS:DX",0
db 0bh,"Set AL=0FFh if input ready, else AL=0",0
db 0ch,"Clear buf and do function in AL",0
db 0dh,"Reset disk",0
db 0eh,"Select drive per DL (0=A)",0
db 0fh,"Open file, FCB at DS:DX",0
db 10h,"Close file, FCB at DS:DX",0
db 11h,"Search for first per pattern FCB at DS:DX",0
db 12h,"Search for next per pattern FCB at DS:DX",0
db 13h,"Delete file per FCB at DS:DX",0
db 14h,"Read sequential, FCB at DS:DX",0
db 15h,"Write sequential, FCB at DS:DX",0
db 16h,"Create file, FCB at DS:DX",0
db 17h,"Rename file, special FCB at DS:DX",0
db 19h,"Return current drive in AL (0=A)",0
db 1ah,"Set DTA to DS:DX",0
db 1bh,"Get FAT info for default drive",0
db 1ch,"Get FAT info for drive DL (0=default)",0
db 21h,"Random Read, FCB at DS:DX",0
db 22h,"Random Write, FCB at DS:DX",0
db 23h,"Set file size per FCB pattern at DS:DX",0
db 24h,"Set random rec field in FCB at DS:DX",0
db 25h,"Set Interrupt <AL> vector to DS:DX",0
db 26h,"Create new segment at <DX>:0000",0
db 27h,"Random Read of <CX> records, FCB at DS:DX",0
db 28h,"Random Write of <CX> records, FCB at DS:DX",0
db 29h,"Parse filename at DS:SI into FCB at ES:DI",0
db 2ah,"Get year to CX, month to DH, day to DL",0
db 2bh,"Set year to CX, month to DH, day to DL",0
db 2ch,"Get CH=hours CL=mins DH=secs DL=tenths",0
db 2dh,"Set hours=CH mins=CL secs=DH tenths=DL",0
db 2eh,"IFF DL=0 then set VERIFY per AL",0
db 2fh,"Get DTA to ES:BX",0
db 30h,"Get DOS version to AL (major), AH (minor)",0
db 31h,"Term/stay resident, DX=# para's AL=exit code",0
db 33h,"Request (AL=0) or set (AL=1,DL=value) BREAK value",0
db 34h,"Set ES:BX to DOS 'in-use' flag",0
db 35h,"Set ES:BX to vector for INT # <AL>",0
db 36h,"Get disk space for drive DL (0=default)",0
db 38h,"Return country info to DS:DX",0
db 39h,"Create directory per ASCIIZ at DS:DX",0
db 3ah,"Remove directory per ASCIIZ at DS:DX",0
db 3bh,"Change directory to ASCIIZ at DS:DX",0
db 3ch,"Create file per ASCIIZ at DS:DX, attrib <CX>",0
db 3dh,"Open file per ASCIIZ at DS:DX, access <AL>",0
db 3eh,"Close file handle <BX>",0
db 3fh,"Read <CX> bytes from file handle <BX> to DS:DX",0
db 40h,"Write <CX> bytes from DS:DX to file handle <BX>",0
db 41h,"Delete file per ASCIIZ at DS:DX",0
db 42h,"LSEEK file handle <BX> <CX:DX> bytes, method <AL>",0
db 43h,"Set (AL=1, CX=value) or get (AL=0) attrib for ASCIIZ at DS:DX",0
db 44h,"IOCTL for file handle <BX>, func <AL>",0
db 45h,"DUP file handle <BX> into <AX>",0
db 46h,"Force DUP of handle <BX> into handle <CX>",0
db 47h,"Get cur dir for drive <DL> (0=default) to DS:SI",0
db 48h,"Allocate <BX> paragraphs, address to AX",0
db 49h,"Free block starting at <ES>",0
db 4ah,"Modify segment <ES> to be <BX> para's in size",0
db 4bh,"EXEC file at ASCIIZ DS:DX, func <AL>",0
db 4ch,"Terminate with exit code <AL>",0
db 4dh,"Get EXIT return code to AX",0
db 4eh,"Find first per ASCIIZ at DS:DX and attrib <CX>",0
db 4fh,"Find next per current DTA",0
db 54h,"Get VERIFY state to AL",0
db 56h,"Rename filename at ASCIIZ DS:DX to ASCIIZ at ES:DI",0
db 57h,"Get (AL=0) or set (AL=1) date/time for file handle <BX>",0
db 59h,"Get extended error code",0
db 5ah,"Create temp file per ASCIIZ DS:DX and attrib <CX>",0
db 5bh,"Create new file per ASCIIZ DS:DX and attrib <CX>",0
db 5ch,"Lock (AL=0) or unlock (AL=1) file handle <BX>",0
db 62h,"Get PSP segment address to BX",080h
int21_line label byte
db 0dh,0ah," DOS: "
db Edit_Call
int21_func db 0
dw offset int21_functab
dw offset table_print
db Edit_End
;**************************************************
;
; Interpret INT 21h BEFORE trace record at [SI]
;
;**************************************************
interp_21 proc near
push dx
push ax
mov ax,[SI].B_ax ;get AX at time of int
mov byte ptr int21_func,ah ;use it to select function
mov dx,offset int21_line
call print_edit
pop ax
pop dx
ret
interp_21 endp
subttl Interpretation - INT 25h and 26h (Absolute disk I/O)
page
;
; Tables used to interpret INT's 25h and 26h
;
int25_functab label byte
db 025h,"Read",0
db 026h,"Write",080h
int25_line label byte
db 0dh,0ah," DOS Absolute "
db Edit_Call
int25_func db 0
dw offset int25_functab
dw offset table_print
db " Drive:"
db Edit_Dec8
int25_drv db 0
db " Sector:"
db Edit_Dec16
int25_sect dw 0
db " #Sectors:"
db Edit_Dec16
int25_numsects dw 0
db " Buf "
db Edit_Word
int25_seg dw 0
db ":"
db Edit_Word
int25_off dw 0
db Edit_End
;**************************************************
;
; Interpret INT 25h or INT 26h BEFORE trace record at [SI]
;
;**************************************************
interp_25 proc near
push dx
push ax
mov al,[SI].B_int ;get INT that was done (25h or 26h)
mov int25_func,al ;move to printline
mov ax,[SI].B_ax ;get drive # from AL
mov int25_drv,al ;move to printline
mov ax,[SI].B_dx ;get starting sector # from DX
mov int25_sect,ax ;move to printline
mov ax,[SI].B_cx ;get # sectors from CX
mov int25_numsects,ax ;move to printline
mov ax,[SI].B_ds ;get buffer segment from DS
mov int25_seg,ax ;move to printline
mov ax,[SI].B_bx ;get buffer offset from BX
mov int25_off,ax ;move to printline
mov dx,offset int25_line ;now print edited line
call print_edit
pop ax
pop dx
ret
interp_25 endp
subttl Periscope Interrupt Interface
page
;*****************************************
;
; This is the interrupt handler use by Periscope to access this code.
; It may also be called by SHIFT-PrtSc.
;
; On entry, AH contains function:
;
; 1 - 8: User Breakpoint checks (BU 1 thru BU 8, then GT)
; 9 - FFh: User exits (/U 9 thru /U FFh)
; 0FFh: Called by PrtSc
;
;*****************************************
db "PS" ;sentinel that Periscope checks for
periscope proc far
;
; First, make very sure that we aren't being re-entered!!! This would
; wipe out our stack which is already in use.
;
cli
test cs:stack_inuse,0ffh
jz periscope2 ;it's ok
periscope1:
mov al,0ffh ;tell Periscope "No Break, No Command to be executed"
iret ;busy, call back later
periscope2:
;
; If we've been entered via Periscope User Break function (during single-stepping,
; with BU 1 thru BU 8 in effect), then exit immediately. Things are slow
; enough without us being executed when we have no Breakpoint checking to do.
;
cmp ah,9 ;BU 1 thru BU 8?
jb periscope1 ;yes, exit
;
; On entry to this periscope int, we ought to save everything but AX,
; and switch to our own stack. Periscope itself doesn't require this,
; but the PrtSc routine assumes it.
;
mov cs:stack_inuse,0ffh ;mark our stack busy
mov cs:save_ss,ss
mov cs:save_sp,sp
mov ss,cs:our_cs
mov sp,offset our_tos
push cs:save_ss ;save old stack stuff for later
push cs:save_sp
push bx
push cx
push dx
push si
push di
push es
push ds
push bp
mov ds,cs:our_cs ;set DS to us for assume ds:code
sti
;
; Give user stats about trace buffer size
;
mov ax,offset last_byte ;get # free bytes
sub ax,next_byte
mov word ptr size_freeb,ax
mov dx,offset size_msg
call print_edit
periscope_menu:
;
; Now display menu and get his selection, until we are to exit
;
mov dx,offset mainmenu
call print_line ;put up main menu
call feed ;extra CRLF's for printer
call key ;get keypress
call do_main ;process it
jc periscope_menu ;we are to loop back
mov dx,offset shadows ;give him exit message
call print_line
periscope_exit:
;
; Restore regs and original stack. AX is already set to return result.
;
cli
pop bp
pop ds
pop es
pop di
pop si
pop dx
pop cx
pop bx
pop cs:save_sp ;restore original stack
pop cs:save_ss
mov ss,cs:save_ss
mov sp,cs:save_sp
mov cs:stack_inuse,0 ;mark our stack not busy
iret
periscope endp
mainmenu db 0dh,0ah
db "(P)rinter (S)creen (E)nable (D)isable (F)CB (T)races (L)ist (C)lear (Q)uit$"
shadows db 0dh,0ah
db "Back... to the shadows... AGAIN!"
db 0dh,0ah,"$"
size_msg label byte
db 0dh,0ah
db "TraceBuf Bytes:"
db Edit_Dec16
dw trace_size
db " Free Bytes:"
db Edit_Dec16
size_freeb dw 0
db Edit_End
subttl INT 05 (SHIFT-PrtSc) Handler
page
;**************************************************
;
; This is another way (besides Periscope) to talk to the tracer, and
; get it to report what it's found. This is not as clean a way as
; via Periscope, but it beats nothing if the Periscope board isn't in
; the system.
;
; This routine just calls the Periscope interrupt handler.
;
;**************************************************
if prt_scr
PrtSc proc far
cli
push es
push ax
mov ax,050h ;set ES to 0050:0000
mov es,ax ;(the print-screen control byte)
cmp byte ptr es:[0],1 ;are we busy with previous request?
jz PrtSc_exit ;yes, don't do anything
mov byte ptr es:[0],1 ;no, mark us busy now
mov ah,0ffh ;call Periscope INT with special arg
int peri_int
mov byte ptr es:[0],0 ;mark us not busy now
PrtSc_exit:
pop ax
pop es
iret
Prtsc endp
endif
subttl Startup (init) code
page
;********************************************************
;
; Startup code, which installs us in memory and sets up interrupts
; to be handled.
;
;********************************************************
init:
mov our_cs,cs ;save for handlers' use
mov test_cs,cs ;start normalized CS for testing
mov ax,offset init ;include all of resident part in it
mov cl,4
shr ax,cl
add test_cs,ax ;done normalizing it
mov dx,offset copyright
mov ah,9
int 021h
;
; Get printer base I/O address for use later
;
mov ax,040h ;point to parallel table at 0040:0008
mov es,ax
mov dx,es:[8] ;get LPT1's base address
mov prt_base,dx ;save it
or dx,dx ;is there an LPT1?
jnz init2 ;yes, move on
mov dx,offset no_printer ;no, give warning message...
call selvideo ;...after switching to video
call print_line
init2:
;
; Init proper I/O mode
;
if use_prt
call selprint
else
call selvideo
endif
;
; Install Periscope access interrupt # 'peri_int'
;
mov al,peri_int ;INT # being installed
mov ah,025h ;DOS "Install Int Vector" func
mov dx,offset periscope ;DS:DX = handler for this INT
int 021h
;
; Install SHIFT-PrtSc interrupt
;
if prt_scr
mov al,5 ;INT # being installed
mov ah,025h ;DOS "Install Int Vector" func
mov dx,offset PrtSc ;DS:DX = handler for this INT
int 021h
endif
;
; Install interrupt vectors for any active ICT's
;
mov cx,8 ;number of ICT's
xor si,si ;Start with ICT # 0
init5:
mov bx,ict_index[si] ;get pointer to an ICT
mov al,[bx].ICT_flags ;get flags to AL
test al,F_ACTIVE ;is this ICT active?
jz init10 ;no, move on to next one
; ------- Validate type of interrupt exit
and al,F_RET+F_RET2+F_IRET
cmp al,F_RET
jz init6 ;this one's legal
cmp al,F_RET2
jz init6 ;this one's legal
cmp al,F_IRET
jz init6 ;this one's legal
mov dx,offset bad_exit ;bad field, give error message
init5b:
;
; Print error message at DS:DX and mark ICT de-activated
;
push dx ;save error message text
mov ax,si ;get ICT # for error message
shr ax,1
and al,7 ;(just in case)
or al,'0' ;make into ASCII digit
mov err_ict,al ;move into error header
mov dx,offset err_msg ;print error header first
call print_line
pop dx ;recover error message itself
call print_line ;display it
xor [bx].ICT_flags,F_ACTIVE ;de-activate this ICT
jmp short init10 ;goto next ICT
init6:
mov al,[bx].ICT_intnum ;get int number to AL
if prt_scr
cmp al,5 ;trying to trace INT 5?
jnz init6b ;no, it's all right
mov dx,offset two_prtscrs ;yes, give error message
jmp init5b
init6b:
endif
mov ah,035h ;get current vector for this INT
push bx ;(save ICT pointer!!!)
int 021h
mov dx,bx ;put vector's offset somewhere safe
pop bx ;(restore ICT pointer!!!)
mov word ptr [bx].ICT_orig_hndlr,dx
mov word ptr [bx].ICT_orig_hndlr+2,es
mov dx,hndlr_index[si] ;DS:DX = new vector for this INT
mov ah,025h ;tell DOS to install it
int 021h ;(intnum still in AL)
init10:
add si,2 ;up to next ICT
loop init5 ;till done all ICT's
;
; List final ICT's
;
mov dx,offset final_msg
call print_line
call disp_active ;display all active ICT's
call crlf
;
; Terminate and stay resident
;
mov dx,offset intro_msg ;give him intro message
call print_line
mov al,peri_int
call print_hex
call crlf
call feed ;extra CRLF's for printer
mov dx,offset init
int 027h
;********************************************************
;
; Startup messages (lost once we're resident)
;
;********************************************************
copyright db 0dh,0ah
db "TRACE - Interrupt Tracer version 1.2 2/26/86"
db 0dh,0ah
db 0dh,0ah
db "Written by Joan Riff for:"
db 0dh,0ah
db "Computerwise Consulting Services P.O. Box 813, McLean VA 22101 (703) 280-2809"
db 0dh,0ah
db 0dh,0ah
db "Placed in the public domain. There ain't one person in a thousand qualified to"
db 0dh,0ah
db "understand or use this thing, so why charge for it?"
db 0dh,0ah,"$"
intro_msg db 0dh,0ah
db 0dh,0ah
db "Trace is now resident."
db 0dh,0ah
db "Use '/U 9' Periscope command"
if prt_scr
db " (or SHIFT-PrtSc)"
endif
db " for access."
db 0dh,0ah
db "When you run Periscope, include command-line arg /I:$"
bad_exit db "has a bad Exit field. ICT deactivated.$"
two_prtscrs db "overlays SHIFT-PrtSc. ICT deactivated.$"
no_printer db 0dh,0ah,"*** Warning: LPT1 not available$"
err_msg db 0dh,0ah,"*** ICT #"
err_ict db "0 $"
final_msg db 0dh,0ah
db 0dh,0ah,"Final ICT's:",0dh,0ah,"$"
code ends
end start